package com.kinglone.util;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;

public class ClassUtil
{
  private static final Logger log = Logger.getLogger(ClassUtil.class);

  private static final Map<String, Field> fieldCache = new ConcurrentHashMap();

  public static boolean isValueType(Class<?> clz)
  {
    try
    {
      if (clz != null)
      {
        return ((!(clz.isPrimitive())) && (!(Number.class.isAssignableFrom(clz))) && (!(Character.class.isAssignableFrom(clz))) && (!(String.class.isAssignableFrom(clz))) && (!(Date.class.isAssignableFrom(clz))) && (!(Boolean.class.isAssignableFrom(clz))));
      }

      throw new RuntimeException("Class can't be null");
    }
    catch (Exception e) {
      log.error("在判断是否是值类型的时候出现了异常：", e); }
    return false;
  }

  public static Object newInstance(String className)
  {
    Object result = null;
    try {
      result = newInstance(Class.forName(className));
    } catch (Exception e) {
      RuntimeException ex = new RuntimeException();
      ex.initCause(e);
      throw ex;
    }
    return result;
  }

  public static Object newInstance(Class c)
  {
    Object result = null;
    try {
      result = c.newInstance();
    } catch (Exception e) {
      RuntimeException ex = new RuntimeException();
      ex.initCause(e);
      throw ex;
    }
    return result;
  }

  public static Field getField(Class clz, String fieldName)
  {
    return getField(clz, fieldName, true);
  }

  public static Field getField(Class clz, String fieldName, boolean exception)
  {
    String key = clz.getName() + " - " + fieldName;
    Field f = (Field)fieldCache.get(key);
    if (f != null) {
      return f;
    }

    for (; clz != Object.class; clz = clz.getSuperclass()) {
      try {
        if (!(Object.class.getName().equals(clz.getName()))) {
          Field field = clz.getDeclaredField(fieldName);
          fieldCache.put(key, field);
          return field;
        }
      }
      catch (NoSuchFieldException e)
      {
      }
    }
    if (exception) {
      throw new RuntimeException("no such field in " + clz.getName());
    }
    return null;
  }

  public static Type[] getActualTypes(Type type)
  {
    if (type instanceof ParameterizedType) {
      ParameterizedType t = (ParameterizedType)type;
      Type[] types = t.getActualTypeArguments();
      if (ObjectUtil.isEmpty(types)) {
        throw new RuntimeException(((Class)type).getName() + " not have ActualType.");
      }
      return types; }
    if (type instanceof Class) {
      Type[] types = null;
      if (isCGLibProxy((Class)type)) {
        Class proxyType = (Class)((Class)type).getGenericSuperclass();
        types = ((ParameterizedType)proxyType.getGenericSuperclass()).getActualTypeArguments();
      } else {
        types = ((ParameterizedType)((Class)type).getGenericSuperclass()).getActualTypeArguments();
      }
      if (ObjectUtil.isEmpty(types)) {
        throw new RuntimeException(((Class)type).getName() + " not have ActualType.");
      }
      return types;
    }
    throw new RuntimeException("type error.");
  }

  public static boolean isCGLibProxy(Class type)
  {
    return type.getName().contains("EnhancerByCGLIB");
  }

  public static <T> Class<T> getActualType(Type type)
  {
    return ((Class)getActualTypes(type)[0]);
  }

  public static boolean actualTypeHasProperty(Class clz, String propertyName)
  {
    try
    {
      Field field = getField(getActualType(clz), propertyName, false);
      if (field != null)
        return true;
    }
    catch (Exception e) {
      log.error("在泛型对象中是否有指定属性的方法中出现了异常：", e);
    }
    return false;
  }

  public static boolean hasProperty(Class clz, String propertyName)
  {
    try
    {
      Field f = getField(clz, propertyName, false);
      if (f != null)
        return true;
    }
    catch (Exception e) {
      log.error("在是否有指定属性的方法中出现了异常：", e);
    }

    return false;
  }

  public static boolean isProperty(Class clz, String property) {
    try {
      PropertyDescriptor descriptor = getProperty(Introspector.getBeanInfo(clz), property);
      if (descriptor != null)
        return true;
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

  public static Class getPropertyType(BeanInfo beanInfo, String property)
  {
    return getProperty(beanInfo, property).getPropertyType();
  }

  public static PropertyDescriptor getProperty(BeanInfo beanInfo, String property)
  {
    PropertyDescriptor[] propertys = beanInfo.getPropertyDescriptors();
    for (PropertyDescriptor propertyDescriptor : propertys) {
      if ((propertyDescriptor.getName().equals(property)) && (!("class".equals(property)))) {
        return propertyDescriptor;
      }
    }
    return null;
  }

  public static PropertyDescriptor getProperty(Class<?> beanClass, String property)
  {
    try
    {
      return getProperty(Introspector.getBeanInfo(beanClass), property);
    } catch (IntrospectionException e) {
      throw new RuntimeException(e);
    }
  }

  public static Object readProperty(Object obj, String property)
  {
    try
    {
      return getProperty(obj.getClass(), property).getReadMethod().invoke(obj, new Object[0]);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}